package com.bruce.ai.alibaba.agentcard.discovery.factory;

import com.alibaba.nacos.api.ai.model.a2a.AgentCard;
import com.bruce.ai.alibaba.agentcard.discovery.client.A2AClient;
import com.bruce.ai.alibaba.agentcard.discovery.config.A2AClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * A2A客户端工厂类
 * 负责创建和管理A2A客户端实例
 * 
 * 特性：
 * - 客户端实例缓存
 * - 配置管理
 * - 生命周期管理
 */
@Component
@ConfigurationProperties(prefix = "a2a.client")
public class A2AClientFactory {
    
    private static final Logger logger = LoggerFactory.getLogger(A2AClientFactory.class);
    
    private final ConcurrentMap<String, A2AClient> clientCache = new ConcurrentHashMap<>();
    private final A2AClientConfig defaultConfig;
    private final String sourceAgentName;
    
    /**
     * 构造函数
     * @param defaultConfig 默认配置
     * @param sourceAgentName 源Agent名称
     */
    @Autowired
    public A2AClientFactory(A2AClientConfig defaultConfig, 
                           @Value("${spring.application.name:agent-card-discovery}") String sourceAgentName) {
        this.defaultConfig = defaultConfig;
        this.sourceAgentName = sourceAgentName;
        logger.info("A2AClientFactory initialized with source agent: {}", sourceAgentName);
    }
    
    /**
     * 创建A2A客户端（使用默认配置）
     * @param agentCard Agent卡片
     * @return A2A客户端
     */
    public A2AClient createClient(AgentCard agentCard) {
        return createClient(agentCard, defaultConfig);
    }
    
    /**
     * 创建A2A客户端（使用自定义配置）
     * @param agentCard Agent卡片
     * @param config 自定义配置
     * @return A2A客户端
     */
    public A2AClient createClient(AgentCard agentCard, A2AClientConfig config) {
        String cacheKey = generateCacheKey(agentCard, config);
        
        return clientCache.computeIfAbsent(cacheKey, key -> {
            logger.info("Creating new A2A client for agent: {}", agentCard.getName());
            return new A2AClient(agentCard, config, sourceAgentName);
        });
    }
    
    /**
     * 获取缓存的客户端
     * @param agentCard Agent卡片
     * @return A2A客户端，如果不存在则返回null
     */
    public A2AClient getCachedClient(AgentCard agentCard) {
        String cacheKey = generateCacheKey(agentCard, defaultConfig);
        return clientCache.get(cacheKey);
    }
    
    /**
     * 移除缓存的客户端
     * @param agentCard Agent卡片
     */
    public void removeClient(AgentCard agentCard) {
        String cacheKey = generateCacheKey(agentCard, defaultConfig);
        A2AClient client = clientCache.remove(cacheKey);
        if (client != null) {
            client.close();
            logger.info("Removed and closed A2A client for agent: {}", agentCard.getName());
        }
    }
    
    /**
     * 清理所有客户端
     */
    public void shutdown() {
        logger.info("Shutting down A2AClientFactory, closing {} clients", clientCache.size());
        
        clientCache.values().forEach(client -> {
            try {
                client.close();
            } catch (Exception e) {
                logger.warn("Error closing A2A client", e);
            }
        });
        
        clientCache.clear();
    }
    
    /**
     * 获取缓存统计信息
     * @return 缓存大小
     */
    public int getCacheSize() {
        return clientCache.size();
    }
    
    /**
     * 检查客户端是否已缓存
     * @param agentCard Agent卡片
     * @return 是否已缓存
     */
    public boolean isCached(AgentCard agentCard) {
        String cacheKey = generateCacheKey(agentCard, defaultConfig);
        return clientCache.containsKey(cacheKey);
    }
    
    /**
     * 生成缓存键
     * @param agentCard Agent卡片
     * @param config 配置
     * @return 缓存键
     */
    private String generateCacheKey(AgentCard agentCard, A2AClientConfig config) {
        return String.format("%s:%s:%d", 
                           agentCard.getName(), 
                           agentCard.getUrl(), 
                           config.hashCode());
    }
    
    /**
     * 创建用于特定认证类型的客户端
     * @param agentCard Agent卡片
     * @param authType 认证类型
     * @param authToken 认证令牌
     * @return A2A客户端
     */
    public A2AClient createClientWithAuth(AgentCard agentCard, 
                                         A2AClientConfig.AuthType authType, 
                                         String authToken) {
        A2AClientConfig config = new A2AClientConfig();
        // 复制默认配置
        copyDefaultConfig(config);
        
        // 设置认证
        config.setAuthType(authType);
        switch (authType) {
            case BEARER:
                config.setBearerToken(authToken);
                break;
            case API_KEY:
                config.setApiKey(authToken);
                break;
            case OAUTH2:
                config.setOauth2Token(authToken);
                break;
            default:
                break;
        }
        
        return createClient(agentCard, config);
    }
    
    /**
     * 复制默认配置到新配置
     * @param config 目标配置
     */
    private void copyDefaultConfig(A2AClientConfig config) {
        config.setConnectTimeoutMs(defaultConfig.getConnectTimeoutMs());
        config.setReadTimeoutMs(defaultConfig.getReadTimeoutMs());
        config.setWriteTimeoutMs(defaultConfig.getWriteTimeoutMs());
        config.setMaxRetries(defaultConfig.getMaxRetries());
        config.setRetryBaseDelayMs(defaultConfig.getRetryBaseDelayMs());
        config.setRetryMaxDelayMs(defaultConfig.getRetryMaxDelayMs());
        config.setCircuitBreakerEnabled(defaultConfig.isCircuitBreakerEnabled());
        config.setCircuitBreakerFailureThreshold(defaultConfig.getCircuitBreakerFailureThreshold());
        config.setCircuitBreakerRecoveryTimeoutMs(defaultConfig.getCircuitBreakerRecoveryTimeoutMs());
        config.setHealthCheckTimeoutMs(defaultConfig.getHealthCheckTimeoutMs());
        config.setMaxIdleConnections(defaultConfig.getMaxIdleConnections());
        config.setKeepAliveDurationMs(defaultConfig.getKeepAliveDurationMs());
    }
}